import re


# 匹配一个数字
print(re.search(r'\d', r'python432\n abc'))
# 匹配单个字符
print(re.search(r'\w', r'python432\n abc'))
# 匹配所有数字
print(re.search(r'\d+', r'python432\n abc')) # 匹配所有数字
print(re.search(r'\D+', r'python432\n abc')) # 匹配所有非数字 相当于 [^0-9]
print(re.search(r'\s+', r'python432\n abc')) # 匹配空白字符
print(re.search(r'\S+', r'python432\n abc')) # 匹配非空白字符 python432\n
print(re.search(r'\w+', r'python432\n abc')) # 匹配所有单个字符 a-z A-Z 0-9 _
print(re.search(r'\W+', r'python432\n abc')) # 匹配 \

print(re.search(r'\d{2}', r'python432\n abc')) # 43
print(re.search(r'[a-zA-Z]+', r'python432\n abc')) # python
print(re.search(r'[a-zA-Z]{2}', r'python432\n abc')) # py


# 边界的元字符
# 边界的元字符
s2 = 'awhahlf@163.com，affafafafaaaaaaaaaaaaaaaa@163.com，abcefg@163.com  afa_@163.com,awhahlfsd@163.com'
# 匹配正确的网页邮箱地址   @ 符号前最多6位最多18位 字母开头的字符

print(re.search(r'^[a-zA-Z]\w{5,17}@163.com',s2)) # ^开始的意思，也有取反的意思 输出   awhahlf@163.com
print(re.findall(r'\b[a-zA-Z]\w{5,17}@163.com\b',s2))  # \b 开始和结尾有单词边界
# 输出 ['awhahlf@163.com', 'abcefg@163.com', 'awhahlfsd@163.com']

# ^ 还有取反的意思
s3 = '1231243243abc'
ret = re.match(r"[1-9]?\d$|100|200",s3)
# 非数字出现 1到n次
print(re.search(r'[^0-9]+',s3)) # ^放在[]里面 表示以非数字开头 输出 abc
print(re.search(r'^[0-9]+',s3)) # ^放在[]前面 表示以数字开头  输出 1231243243

# 分组的元字符
# 含义：1-9之间的数字开始的，出现0此或1次，\d表示以数字结尾的。 | 左边指的是匹配一个2位数，或者 | 右边的数
ret = re.match(r"[1-9]?\d$|100|200",s3)
print(ret)
# 实际匹配结果
# 字符串 s3 = '1231243243abc' 的内容分析：
# 开头是多个数字（1231243243），结尾是字母 abc。
# 正则表达式的三个部分均无法匹配：
# [1-9]?\d$：要求字符串以 1 位或 2 位数字结尾，但 s3 以 abc 结尾 → 不匹配。
# 100：要求字符串以 100 开头，但 s3 以 123 开头 → 不匹配。
# 200：同理，开头不是 200 → 不匹配。
# 因此，ret 的值为 None。
ret1 = re.match(r"[1-9]?\d$|100|200",'200')
print(ret1)

# 在括弧中加入了分支进行判断，根据结果判断，也匹配到结果，
ret2 = re.match(r"\w{4,20}@(163|126|qq)\.com", "test@qq.com")
print(ret2.group())
# 1. 正则表达式结构
# 正则表达式 r"\w{4,20}@(163|126|qq)\.com" 分为以下几个部分：
# \w{4,20}
# \w：匹配任意字母、数字或下划线（等价于 [A-Za-z0-9_]）。
# {4,20}：表示前面的元素（\w）重复 4 到 20 次。
# 整体含义：匹配一个长度在 4 到 20 个字符的用户名（如 test、user123）。
# @
# 直接匹配邮箱地址中的 @ 符号。
# (163|126|qq)
# (...)：定义一个分组（捕获组）。
# |：表示“或”逻辑。
# 整体含义：匹配域名中的服务提供商部分，可以是 163、126 或 qq。
# \.com
# \.：转义字符，匹配字面量的点号 .。
# com：匹配域名的后缀部分。
# 整体含义：匹配 .com 域名。
# 2. re.match 的作用
# re.match 会从字符串的 开头 开始匹配正则表达式。
# 如果匹配成功，返回一个匹配对象（包含匹配的子串等信息）；否则返回 None。
# 在此例中，字符串 "test@qq.com" 完全符合正则表达式规则，因此 ret2 是一个有效的匹配对象。

# html 标签匹配。
ret3 = re.match(r"<([a-zA-Z]+)>\w*</\1>", "<html>hh</html>")
print(ret3.group())
# 1. 正则表达式结构
# 正则表达式 r"<([a-zA-Z]+)>\w*</\1>" 分为以下部分：
#
# <([a-zA-Z]+)>
# <...>：匹配尖括号包裹的标签起始部分。
# ([a-zA-Z]+)：捕获组，匹配一个或多个字母（大小写不敏感），捕获标签名（如 html）。
# 整体含义：匹配类似 <html> 的开始标签，并捕获标签名。
# \w*
# \w：匹配任意字母、数字或下划线（等价于 [A-Za-z0-9_]）。
# *：表示前面的元素（\w）重复 0 次或多次。
# 整体含义：匹配标签内的内容（如 hh）。
# </\1>
# </...>：匹配尖括号包裹的标签结束部分。
# \1：反向引用，表示引用第一个捕获组（即 ([a-zA-Z]+)）匹配的内容。
# 整体含义：匹配结束标签（如 </html>），确保开始和结束标签名称一致。
# 2. 匹配过程详解
# 以字符串 "<html>hh</html>" 为例：
#
# 开始标签匹配
# 正则 <([a-zA-Z]+)> 匹配 <html>。
# 捕获组 1 得到 html。
# 内容匹配
# \w* 匹配 hh（两个字母）。
# 结束标签匹配
# </\1> 匹配 </html>，因为 \1 替换为捕获组 1 的值 html。
# 最终，整个字符串完全匹配正则表达式。



ret4 = re.match(r"<(?P<my_name>[a-zA-Z]+)>\w*</(?P=my_name)>", "<html>hh</html>")
print(ret4.group())
# 1. 正则表达式结构
# 正则表达式 r"<(?P<my_name>[a-zA-Z]+)>\w*</(?P=my_name)>" 分为以下部分：
#
# (?P<my_name>[a-zA-Z]+)
# ?P<my_name>：命名捕获组的语法，将捕获的内容命名为 my_name。
# [a-zA-Z]+：匹配一个或多个字母（大小写不敏感）。
# 整体含义：捕获标签名（如 html）并命名为 my_name。
# <...> 和 </...>
# 匹配 HTML/XML 标签的开始和结束部分。
# \w*
# 匹配任意字母、数字或下划线（等价于 [A-Za-z0-9_]），重复 0 次或多次。
# 含义：匹配标签之间的内容（如 hh）。
# (?P=my_name)
# 命名反向引用：引用之前命名捕获组 my_name 的值。
# 含义：确保结束标签与开始标签名称一致（如 </html>）。
# 2. 匹配过程详解
# 以字符串 "<html>hh</html>" 为例：
#
# 开始标签匹配
# (?P<my_name>[a-zA-Z]+) 匹配 html，并将其命名为 my_name。
# 此时 my_name 的值为 "html"。
# 内容匹配
# \w* 匹配 hh（两个字母）。
# 结束标签匹配
# (?P=my_name) 替换为 my_name 的值（即 html），最终匹配 </html>。
# 整个字符串完全匹配正则表达式。
#
# 3. 命名捕获组的优势
# 可读性：通过命名（如 my_name）代替数字索引（如 \1），代码更易理解。
# 维护性：复杂正则中，命名组能避免数字索引的混淆。